﻿#region Librerias

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using ESF.General;
using System.Configuration;

#endregion


namespace ESF.Data
{
	public sealed class ContextSingleTransactionScope : IDisposable
	{
		#region Miembros

		private static Dictionary<string, List<string>> _Callers = new Dictionary<string,List<string>> ();

		private static Dictionary <string, DbProviderFactory> _DbFactories = new Dictionary<string,DbProviderFactory> ();

		private static Dictionary <string, IDbConnection> _Connections = new Dictionary<string,IDbConnection> ();

		private static Dictionary <string, IDbTransaction> _Transactions = new Dictionary<string,IDbTransaction> ();

		private string _CallerID;

		private string _ConnectionName;

		private Stopwatch _Stopwatch;

		private string [] _ConnectionParams;

		#endregion


		#region Constructor

		public ContextSingleTransactionScope (string pConnectionName, params string [] pParams) 
			: this (pConnectionName, IsolationLevel.ReadCommitted) {}


		public ContextSingleTransactionScope (string pConnectionName, IsolationLevel pIsolationLevel, params string [] pParams)
		{
			DbProviderFactory tmpFactory;
			
			IDbConnection tmpConnection = null;
			
			IDbTransaction tmpTransaction = null;

			ConnectionStringSettings cnnSettings;

			string key;


			_ConnectionParams = pParams;

			key = pConnectionName + Thread.CurrentThread.ManagedThreadId;

			cnnSettings = ReadConfig.GetConnectionSetting (pConnectionName);

			_CallerID = Guid.NewGuid ().ToString ();

			_ConnectionName = pConnectionName;

			_Stopwatch = new Stopwatch ();
			_Stopwatch.Start ();
			
			if (!_DbFactories.ContainsKey (key))
			{
				tmpFactory = DbProviderFactories.GetFactory (cnnSettings.ProviderName);

				_DbFactories.Add (key, tmpFactory);
			}
			else
			{
				tmpFactory = _DbFactories [key];
			}

			if (!_Connections.ContainsKey (key))
			{
				try
				{
					tmpConnection = tmpFactory.CreateConnection ();
					tmpConnection.ConnectionString = string.Format (cnnSettings.ConnectionString, pParams);

					tmpConnection.Open ();

					tmpTransaction = tmpConnection.BeginTransaction (pIsolationLevel);
				}
				catch (Exception)
				{
					DbCommon.Stop (pConnectionName, tmpTransaction, tmpConnection, null);
					
					throw;
				}

				_Connections.Add (key, tmpConnection);

				_Transactions.Add (key, tmpTransaction);
			}

			if (!_Callers.ContainsKey (key))
			{
				_Callers.Add (key, new List<string> ());
			}

			_Callers [key].Add (_CallerID);
		}

		#endregion


		#region Funciones

		public static bool GetCurrentTransaction (string pConnectionName, out IDbTransaction pTransaction, out IDbConnection pConnection, params string [] pParams)
		{
			string key;


			key = pConnectionName + Thread.CurrentThread.ManagedThreadId;

			if (_Connections.ContainsKey (key))
			{
				pConnection = _Connections [key];
				pTransaction = _Transactions [key];

				return true;
			}
			// no hay contexto
			else
			{
				ConnectionStringSettings cnnSettings = ReadConfig.GetConnectionSetting (pConnectionName);

				pConnection = DbProviderFactories.GetFactory (cnnSettings.ProviderName).CreateConnection ();
				pConnection.ConnectionString = string.Format (cnnSettings.ConnectionString, pParams);

				pConnection.Open ();

				pTransaction = pConnection.BeginTransaction ();

				return false;
			}
		}


		public void Complete ()
		{
			string key;


			key = _ConnectionName + Thread.CurrentThread.ManagedThreadId;

			if (_Callers [key].First () == _CallerID)
			{
				DbCommon.Confirm (_ConnectionName, _Transactions [key], _Connections [key], null);

				_DbFactories.Remove(key);
				_Callers.Remove (key);
				_Transactions.Remove (key);
				_Connections.Remove (key);
			}
		}

		#endregion

		
		#region Dispose
		
		public void Dispose()
		{
			string key;


			key = _ConnectionName + Thread.CurrentThread.ManagedThreadId;

			if (_Callers.ContainsKey (key) && _Callers [key].First () == _CallerID)
			{
				DbCommon.Stop (_ConnectionName, _Transactions [key], _Connections [key], null);

				_DbFactories.Remove(key);
				_Callers.Remove (key);
				_Transactions.Remove (key);
				_Connections.Remove (key);
			}
		}

		#endregion
	}
}
